#include <llvm/Support/IRBuilder.h>
#include <llvm/Analysis/Verifier.h>

#include "RModule.h"
#include "RCompiler.h"
#include "Reactor.h"
#include "util.h"
#include "Symbol.h"
#include "Evaluator.h"
#include "Environment.h"
#include "Promise.h"
#include "Closure.h"

using namespace R;

#define NEXT_OPCODE()    cs[pc++]
#define PARM(d)          cs[pc+d]
#define GETLBL(d)        cs[pc+d]


/*
 *  Macros for createBBs function
 */

#define dolabel(n) ({																										\
				if(!bbs[n - begin]) {																						\
					char buf[64];																									\
					sprintf(buf, "label-0x"XWORD, n);															\
					jitddo(printf("create label: %s - "WORD" "WORD"\n", buf, n, n - begin);)	\
					bbs[n - begin] = llvm::BasicBlock::Create(func->getContext(), buf, func);	\
				}																																\
			})


#define CL_IOL(d, k) 
#define CL_IMM(d, k) 
#define CL_IDX(d, k) 
#define CL_IFI(d, k) 
#define CL_LBL(d, k) {                                                  \
      uint64_t dest = GETLBL(d);                                        \
      if(dest >= begin && dest < end) {                                 \
        dolabel(dest);                                                  \
      }                                                                 \
    }

#define CL_CF_NOP()
#define CL_USE_EVALSTACK()
#define CL_CF_BT(idx) if((pc) < end) dolabel(pc); /* pc has been incremented in CL_PN(...) */
#define CL_CF_RET()   if((pc) < end) dolabel(pc);

#define CL_P0()
#define CL_P1(__t1)                   CL_##__t1(0, 1); pc += 1
#define CL_P2(__t1, __t2)             CL_##__t1(0, 2); CL_##__t2(1, 2); pc += 2
#define CL_P3(__t1, __t2, __t3)       CL_##__t1(0, 3); CL_##__t2(1, 3); CL_##__t3(2, 3); pc += 3
#define CL_P4(__t1, __t2, __t3, __t4) CL_##__t1(0, 4); CL_##__t2(1, 4); CL_##__t3(2, 4); CL_##__t4(3, 4); pc += 4
#define APPLY_BC(__x, __p, __cf) case BC_ ## __x: { CL_##__p; CL_##__cf; } break;

/*
 * End macros
 */

void RModule::createBBs(llvm::Function* func, llvm::BasicBlock** bbs, uint64_t begin, uint64_t end) {
	uint64_t           pc = begin;
	uint64_t           bcode;

	bbs[0] = llvm::BasicBlock::Create(func->getContext(), "entry", func);

  do {
    bcode = NEXT_OPCODE();
		switch(bcode) {
#include "bc.def"
			default:
				rfatal("Bcode %s (0x%llx) not implemented", BC_NAME(bcode), bcode);
		}

	} while(pc < end);
}


void RModule::generateCode(llvm::Function* func, uint64_t funcNo) {
	uint64_t begin = funInfos[funcNo].start;
	uint64_t end = funInfos[funcNo].end + 1;
	llvm::BasicBlock* bbs[end -begin];

	for(uint32_t i=0; i<(end - begin); i++)
		bbs[i] = 0;

	createBBs(func, bbs, begin, end);

	RCompiler* compiler = vm->compiler;
	llvm::Module* mod = compiler->selfModule;
	ReactorIntrinsics* intrinsics = compiler->getIntrinsics();

	llvm::Function::arg_iterator args = func->arg_begin();
	llvm::IRBuilder<> builder(bbs[0]);

	uint64_t pc = begin;
	uint64_t bcode;

	jitddo(printf("generating code of %s of function at pc "WORD" with "WORD" bytes (ends at "WORD")\n", func->getName().data(), begin, end - begin, end);)

	llvm::Value* eval = builder.CreateCall(compiler->getEvaluator);
	llvm::Value* stack = builder.CreateCall(compiler->getStack, eval);

  do {
		if(bbs[pc-begin]) {
			jitddo(printf("switching to bb["WORD"]: %s\tpc = "WORD"\n", pc-begin, bbs[pc-begin]->getName().data(), pc);)
			// BB's terminator is null, branch with the following block before switching to it.
			if( (builder.GetInsertBlock() != bbs[pc-begin]) && (!builder.GetInsertBlock()->getTerminator()) ) {
				builder.CreateBr(bbs[pc-begin]);
			}
			builder.SetInsertPoint(bbs[pc-begin]);
		}

    bcode = NEXT_OPCODE();

#define CC_IOL(v, d) llvm::Value* v = llvm::ConstantInt::get(intrinsics->typeInt64, 0)
#define CC_IMM(v, d) llvm::Value* v = llvm::ConstantInt::get(intrinsics->typeInt64, PARM(d))
#define CC_IFI(v, d) llvm::Value* v = \
			llvm::ConstantExpr::getIntToPtr(llvm::ConstantInt::get(intrinsics->typeInt64, (uintptr_t)(&funInfos[PARM(d)])), intrinsics->typeFunInfo)
#define CC_LBL(v, d) uint64_t dest_##v = GETLBL(d); llvm::Value* v = llvm::ConstantInt::get(intrinsics->typeInt64, dest_##v)
#define CC_IDX(v, d) llvm::Value* v = \
			llvm::ConstantExpr::getIntToPtr(llvm::ConstantInt::get(intrinsics->typeInt64, (uintptr_t)(cpool.data + PARM(d))), intrinsics->typepAny)

#define CC_P0()																													\
		llvm::Value* res = builder.CreateCall(compiler->evalFunctions[bcode], eval)
		
#define CC_P1(__t1)																											\
		CC_##__t1(v0, 0);																										\
		pc += 1;																														\
		llvm::Value* res = builder.CreateCall2(compiler->evalFunctions[bcode], eval, v0)

#define CC_P2(__t1, __t2)																								\
		CC_##__t1(v0, 0);																										\
		CC_##__t2(v1, 1);																										\
		pc += 2;																														\
		llvm::Value* res = builder.CreateCall3(compiler->evalFunctions[bcode], eval, v0, v1)
		
#define CC_P3(__t1, __t2, __t3)																					\
		CC_##__t1(v0, 0);																										\
		CC_##__t2(v1, 1);																										\
		CC_##__t3(v2, 2);																										\
		pc += 3;																														\
		llvm::Value* res = builder.CreateCall4(compiler->evalFunctions[bcode], eval, v0, v1, v2)

#define CC_P4(__t1, __t2, __t3, __t4)																		\
		CC_##__t1(v0, 0);																										\
		CC_##__t2(v1, 1);																										\
		CC_##__t3(v2, 2);																										\
		CC_##__t4(v3, 3);																										\
		pc += 4;																														\
		llvm::Value* res = builder.CreateCall5(compiler->evalFunctions[bcode], eval, v0, v1, v2, v3)

#define CC_CFG_CF_NOP()

#define CC_CFG_CF_RET() llvm::Value* retval = builder.CreateCall2(compiler->anyPop, stack, llvm::ConstantInt::get(intrinsics->typeInt64, 1));\
		builder.CreateRet(retval); funInfos[funcNo].isCall = (bcode == BC_RETURNJMP)

#define CC_CFG_CF_BT(i) \
		if(bcode != BC_GOTO) {\
			llvm::Value* cond = builder.CreateICmpEQ(res, llvm::ConstantInt::get(intrinsics->typeInt64, 0));\
			builder.CreateCondBr(cond, bbs[pc-begin], bbs[dest_v##i-begin]);\
		}else{\
			builder.CreateBr(bbs[dest_v##i-begin]);\
		}

#define CC_CFG_USE_EVALSTACK() uint64_t nbArgs = cs[pc-1];\
		llvm::AllocaInst** params = new llvm::AllocaInst*[nbArgs];\
		llvm::CallInst* temp = NULL;\
		llvm::Instruction* beginInst = &(builder.GetInsertBlock()->getParent()->front().front());\
		int i;\
		llvm::CallInst* call = static_cast<llvm::CallInst*>(res);\
		for(i = 0; i < nbArgs; i++) {\
			params[i] = new llvm::AllocaInst(intrinsics->typeAny, "", beginInst);\
			new llvm::StoreInst(\
					llvm::ConstantExpr::getIntToPtr(llvm::ConstantInt::get(intrinsics->typeInt64, 0), intrinsics->typeAny),\
					params[i],\
					beginInst);\
			llvm::Value* GCArgs[2] = { new llvm::BitCastInst(params[i], intrinsics->ptrPtrType, "", beginInst), intrinsics->constantPtrNull};\
			llvm::CallInst::Create(intrinsics->llvm_gc_gcroot, GCArgs, "", beginInst);\
		}\
		llvm::Value *PopArgs[] = { stack, llvm::ConstantInt::get(intrinsics->typeInt64, 1) };\
		for(i = 0; i < nbArgs; i++) {\
			temp = llvm::CallInst::Create(compiler->anyPop, PopArgs, "", call);\
			new llvm::StoreInst(temp, params[i], call);\
		}\
		while(i--){\
			llvm::Value *PushArgs[] = { stack, new llvm::LoadInst(params[i], "", false, call)};\
			llvm::CallInst::Create(compiler->JITPush, PushArgs, "", call);\
		}\
		delete [] params;\
		builder.CreateCall2(compiler->JITPop, stack, llvm::ConstantInt::get(intrinsics->typeInt64, nbArgs))

#define APPLY_BC(__x, __p, __cf) case BC_ ## __x: { jitddo(printf("Generate a %s\n", #__x);) CC_ ## __p; \
	CC_CFG_##__cf;\
	} break;

		switch(bcode){
#include "bc.def"
			default:
				rfatal("Bcode %s (0x%llx) not implemented", BC_NAME(bcode), bcode);
		}
		jitddo(printf("Analyzing bytecode %s, pc = "WORD"\n", BC_NAME(bcode), pc);)
  } while(pc < end);
	jitddo(printf("---------- code of generated function ----------\n");)
//	jitddo(fflush(NULL); func->dump();)
	jitddo(rerror_on(llvm::verifyFunction(*func, llvm::PrintMessageAction), "Error on verification. Bad function generation on jit for \"%s\"", func->getName().data());)
	jitddo(printf("----------         optimizing           --------\n");)
  compiler->pm->run(*func);
  jitddo(printf("----------       end optimization       --------\n");)
//  jitddo(fflush(NULL); func->dump();)
  jitddo(printf("----------    verifying function        --------\n");)
  jitddo(rerror_on(llvm::verifyFunction(*func, llvm::PrintMessageAction), "Error on verification. Bad function generation on jit for \"%s\"", func->getName().data());)
  jitddo(printf("----------   end function verification  --------\n");)
  funInfos[funcNo].jitFun = func;
}
